 /********************************************************************************************\
 * libeds - extensible data structures library                                                *
 * Copyright (C) 2006, Dan Kruchinin, <just.asgard@gmail.com>                                 *
 *                                                                                            *
 * -> errors.c                                                                                *
 * errors control system implementation.                                                      *
 * keep in mind, user should get negative error codes, but inside error control system        *
 * error codes must be positive.                                                              *
 *                                                                                            *
 * This library is free software; you can redistribute it and/or modify it under the terms of *
 * the GNU Lesser General Public License as published by the Free Software Foundation;        *
 * either version 2.1 of the License, or (at your option) any later version.                  *
 *                                                                                            *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;  *
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  *
 * See the GNU Lesser General Public License for more details.                                *
 *                                                                                            *
 * You should have received a copy of the GNU Lesser General Public License along with this   *
 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,  *
 * Boston, MA 02111-1307 USA                                                                  *
 \********************************************************************************************/

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include "safe_alloc.h"
#include "errors.h"

/* "errno" variable will has name "ds_errno" */
static ds_errs_t _errs = { .inited = 0 };
static pthread_mutex_t _errslock = PTHREAD_MUTEX_INITIALIZER;
static int __last_pos = DS_ERRPOS_MAIN, __err_sys;

/* here error messages holds. message number in this array must be equal to error code * -1 */
static char* __ds_errs[NDSERRS] = {
  "data structure, passed to function doesn't exists!",                           /* DSERR_EMPTY_DS */
  "node is empty!",                                                               /* DSERR_EMPTY_NODE */
  "your data structure has not compare function. please register it.",            /* DSERR_NO_CMP_FUNC */
  "your data structures are different, hence they can not be joined",             /* DSERR_DS_JDIFF */
  "item duplicating."                                                             /* DSERR_DUPLICATE */
};

static void __init_errors_vector(void);
static void __increase_errors_vector(void);
static void __destroy_errors_vector(void);
static void __init_errno(ds_errno_t*, char**, const int);

/* return error code of last occured error */
int ds_errcode(void)
{
  return _ds_get_errno(__last_pos);
}

/* return error message, that equal to error code */
char* ds_strerror(const int errcode)
{
  char* msg;

  pthread_mutex_lock(&_errslock);

  if(!_errs.inited)
	__init_errors_vector();
  if(errcode == DSERR_NOERRS)
	return DSERR_NOERRS_MSG;
  if(errcode == DSERR_SYSTEM)
	return strerror(__err_sys);
  
  if((_err(errcode) < 0) || (_err(errcode) >= _errs.errcodes[__last_pos].errnum)) {
	pthread_mutex_unlock(&_errslock);
	return NULL;
  }
  
  msg = _errs.errcodes[__last_pos].errdescr[_err(errcode)];
  pthread_mutex_unlock(&_errslock);

  return msg;
}

/* return error message, that equal to code last occured error */
char* ds_errmsg(void)
{
  int errcode;
  char* msg;

  pthread_mutex_lock(&_errslock);
  
  if(!_errs.inited)
	__init_errors_vector();
  if((errcode = _errs.errcodes[__last_pos].errcode) == DSERR_NOERRS) {
	pthread_mutex_unlock(&_errslock);
	return DSERR_NOERRS_MSG;
  }
  if(errcode == DSERR_SYSTEM) {
	pthread_mutex_unlock(&_errslock);
	return strerror(__err_sys);
  }

  msg = _errs.errcodes[__last_pos].errdescr[_err(errcode)];
  pthread_mutex_unlock(&_errslock);

  return msg;
}

/* using on new data structure implementing. using this function you can change error code value */
void _ds_set_errno(const int errcode, const int errpos)
{
  pthread_mutex_lock(&_errslock);

  if(!_errs.inited)
	__init_errors_vector();
  if(errcode == DSERR_SYSTEM)
	__err_sys = errno;
  
  _errs.errcodes[errpos].errcode = errcode;
  __last_pos = errpos;
  pthread_mutex_unlock(&_errslock);
}

int _ds_get_errno(const int errpos)
{
  int errcode;
  
  pthread_mutex_lock(&_errslock);

  if(!_errs.inited)
	__init_errors_vector();
  
  errcode = _errs.errcodes[errpos].errcode;
  pthread_mutex_unlock(&_errslock);

  return errcode;
}

int _ds_pos_is_reg(const int pos)
{
  pthread_mutex_lock(&_errslock);

  if(!_errs.inited)
	__init_errors_vector();
  if((pos >= 0) && (pos <= _errs.cur_pos)) {
	pthread_mutex_unlock(&_errslock);
	return 1;
  }

  pthread_mutex_unlock(&_errslock);
  return 0;
}

int _ds_reg_errno_pos(char** descr, const int n)
{
  int pos;
  
  pthread_mutex_lock(&_errslock);

  if(!_errs.inited)
	__init_errors_vector;
  if((_errs.cur_pos + 1) >= _errs.n)
	__increase_errors_vector();

  pos = ++_errs.cur_pos;
  __init_errno(&_errs.errcodes[pos], descr, n);
  
  pthread_mutex_unlock(&_errslock);

  return pos;
}

static void __init_errors_vector(void)
{
  _errs.errcodes = (ds_errno_t*)scalloc(ERRVN, sizeof(*(_errs.errcodes)));
  _errs.n = ERRVN;
  _errs.cur_pos = 0;
  _errs.inited = 1;
  __init_errno(&_errs.errcodes[0], __ds_errs, NDSERRS);
  atexit(__destroy_errors_vector);
}

static void __increase_errors_vector(void)
{
  _errs.n *= 2;
  _errs.errcodes = (ds_errno_t*)srealloc(_errs.errcodes, _errs.n);
}

static void __destroy_errors_vector(void)
{
  if(_errs.errcodes != NULL)
	sfree(_errs.errcodes);
}

static void __init_errno(ds_errno_t* err, char** descr, const int n)
{
  err->errcode = DSERR_NOERRS;
  err->errdescr = descr;
  err->errnum = n;
}

